package com.yfbao.horizon.schedule.tk.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.reflect.MethodSignature;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

/**
 * Created by lijz
 * Date 2021/4/13 4:32 下午
 */
public class AbstractMetricMethodAspect {
    private static final String PROXY_FLAG = "$";
    private static final String PROXY_FLAG_ = "\\$";

    protected Method resolveMethod(ProceedingJoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Class<?> targetClass = joinPoint.getTarget().getClass();

        Method method = getDeclaredMethodFor(targetClass, signature.getName(),
                signature.getMethod().getParameterTypes());
        if (method == null) {
            throw new IllegalStateException("Cannot resolve target method: " + signature.getMethod().getName());
        }
        return method;
    }

    protected String resolveSimpleClassName(ProceedingJoinPoint joinPoint) {
        String targetClass = joinPoint.getTarget().getClass().getSimpleName();
        if (targetClass.contains(PROXY_FLAG)) {
            targetClass = targetClass.split(PROXY_FLAG_)[0];
        }
        return targetClass;
    }

    protected <T extends Annotation> T getMethodAnnotation(ProceedingJoinPoint pjp, Class<T> annotationClass) {
        Method originMethod = resolveMethod(pjp);
        if (null != originMethod.getAnnotation(annotationClass)) {
            return originMethod.getAnnotation(annotationClass);
        }
        Signature signature = pjp.getSignature();
        Method method = ((MethodSignature) signature).getMethod();
        if (null != method.getAnnotation(annotationClass)) {
            return method.getAnnotation(annotationClass);
        }
        return null;
    }

    /**
     * Get declared method with provided name and parameterTypes in given class and its super classes.
     * All parameters should be valid.
     *
     * @param clazz          class where the method is located
     * @param name           method name
     * @param parameterTypes method parameter type list
     * @return resolved method, null if not found
     */
    private Method getDeclaredMethodFor(Class<?> clazz, String name, Class<?>... parameterTypes) {
        try {
            return clazz.getDeclaredMethod(name, parameterTypes);
        } catch (NoSuchMethodException e) {
            Class<?> superClass = clazz.getSuperclass();
            if (superClass != null) {
                return getDeclaredMethodFor(superClass, name, parameterTypes);
            }
        }
        return null;
    }
}
